﻿using SharpDepend.Datatypes;
using System.Text;
using System;

namespace SharpDepend.Utils
{
    public static class Math
    {
        public static double ToRadian = System.Math.PI / 180.0;

        public static double ToDegrees = 180.0 / System.Math.PI;

        public static class Parse
        {
            public static double Double(StringBuilder value)
            {
                // TODO: Optimize!
                return Double(value.ToString());
            }

            public static double Double(string value)
            {
                bool isNegative = false;
                if (value.Length > 0 && value[0] == '-')
                {
                    isNegative = true;
                    value = value.Substring(1);
                }

                int dot = value.LastIndexOf(',');

                if (dot == -1)
                {
                    dot = value.LastIndexOf('.');

                    if (dot == -1)
                    {
                        if (isNegative)
                        {
                            return -Int(value);
                        }

                        return Int(value); // Just parse the integer number.
                    }
                }

                // Code is running here if a dot or comma is there.

                if (value.LastIndexOf('+') > -1 || value.LastIndexOf('-') > -1)
                {
                    double toReturn = double.Parse(value);

                    return toReturn;
                }

                string integer = value.Substring(0, dot);
                string decimals = value.Substring(dot + 1);

                long intInteger = long.Parse(integer);
                long intDecimals = decimals.Length > 0 ? long.Parse(decimals) : 0;

                long splitIn = 1;

                for (int i = 0; i < decimals.Length; i++)
                {
                    splitIn *= 10;
                }

                double result = intDecimals / (double)splitIn + intInteger;

                if (isNegative)
                {
                    return -result;
                }

                return result;
            }

            public static int Int(string value)
            {
                int negative = 0;
                if (value.Length > 0 && value[0] == '-')
                {
                    negative = 1; // Start at next position.
                }

                int result = 0;
                for (int i = negative; i < value.Length; i++)
                {
                    result = 10 * result + (value[i] - 48);
                }
                return result * (negative == 1 ? -1 : 1);
            }
        }

        public static void GetOptimizedGrid(int count, out int rows, out int columns)
        {
            columns = (int)System.Math.Sqrt(count);
            rows = (int)System.Math.Ceiling(count / (double)columns);
        }

        public static void Swap<T>(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; }

        public static void Flip(ref double x1, ref double y1, ref double x2, ref double y2)
        {
            if (x1 > x2)
            {
                double temp = x2;
                x2 = x1;
                x1 = temp;
            }
            if (y1 > y2)
            {
                double temp = y2;
                y2 = y1;
                y1 = temp;
            }
        }

        public static Vector2 GetClosestPointOnLineSegment(ref Vector2 A, ref Vector2 B, ref Vector2 P)
        {
            Vector2 AP = P - A;       //Vector from A to P    
            Vector2 AB = B - A;       //Vector from A to B   

            double magnitudeAB = AB.LengthSquared();     //Magnitude of AB vector (it's length squared)      
            double ABAPproduct = Vector2.Dot(AP, AB);    //The DOT product of a_to_p and a_to_b      
            double distance = ABAPproduct / magnitudeAB; //The normalized "distance" from a to your closest point   

            if (distance < 0)     //Check if P projection is over vectorAB      
            {
                return A;

            }
            else if (distance > 1)
            {
                return B;
            }
            else
            {
                return A + AB * distance;
            }
        }

        public static double Distance(double x1, double y1, double x2, double y2)
        {
            return (float)System.Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
        }

        public static double Distance(Vector2 p1, Vector2 p2)
        {
            return System.Math.Sqrt(System.Math.Pow(p2.X - p1.X, 2) + System.Math.Pow(p2.Y - p1.Y, 2));
        }

        public static double Distance(Vector3 point1, Vector3 point2)
        {
            return (float)System.Math.Sqrt((point1.X - point2.X) * (point1.X - point2.X) + (point1.Y - point2.Y) * (point1.Y - point2.Y) + (point1.Z - point2.Z) * (point1.Z - point2.Z));
        }

        static string[] sPreIntegerStrings = new string[1024];

        static Math()
        {
            for (int i = sPreIntegerStrings.Length - 1; i >= 0; i--)
            {
                sPreIntegerStrings[i] = i.ToString();
            }
        }

        /// <summary>
        /// Optimised in range between 0 to 128.
        /// </summary>
        /// <param name="integer">Integer to string.</param>
        /// <returns>String.</returns>
        public static string ToStringFast(this int integer)
        {
            if (integer >= 0 && integer < sPreIntegerStrings.Length)
            {
                return sPreIntegerStrings[integer];
            }
            return integer.ToString();
        }
    }
}
